/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./BSplineCurve.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
BSplineCurve::BSplineCurve(): BezierCurve() {
	mPolygonColor = RGB(247, 160, 8);
	mColor = RGB(255, 0, 0);
	mBasisPrecision = 30;
	mBSplineBasis = true;
	mBSplineMode = true;
}

BSplineCurve::BSplineCurve(cBool arePointsVisible, cBool isPolygonVisible, cWinColor color):
			BezierCurve(arePointsVisible, isPolygonVisible, color) {
	mPolygonColor = color;
	mIsPolygonVisible = isPolygonVisible;
	mArePointsVisible = arePointsVisible;
	mColor = RGB(255, 0, 0);
	mBasisPrecision = 30;
	mBSplineBasis = true;
	mBSplineMode = true;
}
    
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
/*virtual*/ cVoid BSplineCurve::AddPoint(Point4D p, cUShort selected) {
	PointsSet::PointPair node;
	switch(selected) {
		default:
		case 0:
			node.mSelected = NO_SELECTION;
			break;
		case 1:
			node.mSelected = TEMP_SELECTION;
			break;
		case 2:
			node.mSelected = PERS_SELECTION;
			break;
	}

	if (mBSplineMode) {
		node.mPoint = p;
		mBSplineNodes.Add(node);
		if (!mBSplineBasis) {
			ConvertFromBSpline();
		} else {
			CalculateBasis();
		}
	} else {
		if (mBNodes.GetSize() < 4) {
			node.mPoint = p;
			mBNodes.Add(node);
		} else {
			AddContPoints();
			node.mPoint = p;
			mBNodes.Add(node);
		}
	}
}
	
/*virtual*/ cVoid BSplineCurve::AddPoint(Point4D p) {
	PointsSet::PointPair node;
	node.mSelected = NO_SELECTION;

	if (mBSplineMode) {
		node.mPoint = p;
		mBSplineNodes.Add(node);
		if (!mBSplineBasis) {
			ConvertFromBSpline();
		} else {
			CalculateBasis();
		}
	} else {
		if (mBNodes.GetSize() < 4) {
			node.mPoint = p;
			mBNodes.Add(node);
		} else {
			AddContPoints();
			node.mPoint = p;
			mBNodes.Add(node);
		}
	}
}

/*virtual*/ cVoid BSplineCurve::RemovePoint(cInt id) {
	if (mBSplineMode) {
		mBSplineNodes.Delete(id);
		if (!mBSplineBasis) {
			ConvertFromBSpline();
		} else {
			CalculateBasis();
		}
	} else if (id == mBNodes.GetSize() - 1) {
		if (mBNodes.GetSize() > 4) {
			mBNodes.Delete(mBNodes.GetSize() - 1);
			mBNodes.Delete(mBNodes.GetSize() - 1);
			mBNodes.Delete(mBNodes.GetSize() - 1);
		} else {
			mBNodes.Delete(id);
		}
	}
}

/*virtual*/ Point4D BSplineCurve::GetPoint(cInt id) {
	if (mBSplineMode) {
		return mBSplineNodes[id].mPoint;
	} else {
		return mBNodes[id].mPoint;
	}
}

/*virtual*/ cVoid BSplineCurve::ReplacePoint(const Point4D& p, cInt id) {
	if (mBSplineMode) {
		mBSplineNodes[id].mPoint = p;
		if (!mBSplineBasis) {
			ConvertFromBSpline();
		}
	} else {
		if (id == mBNodes.GetSize() - 1 || id == 0) {
			mBNodes[id].mPoint = p;
		}
	}
}

cInt BSplineCurve::GetNrOfPoints() {
	if (mBSplineMode) {
		return mBSplineNodes.GetSize();
	} else {
		return mBNodes.GetSize();
	}
}

/*virtual*/ Point4D BSplineCurve::GetCurvePoint(cInt iIndex) {
	return GetPoint(iIndex);
}

/*virtual*/ cInt BSplineCurve::GetCurveNrOfPoints() {
	return GetNrOfPoints();
}

cBool BSplineCurve::IfBSplineBasis() {
	return mBSplineBasis;
}

cVoid BSplineCurve::SetIfBSplineBasis(cBool v) {
	if (mBSplineMode) {
		if (mBSplineBasis && !v) {
			ConvertFromBSpline();
		} else if (!mBSplineBasis && v) {
			CalculateBasis();
		}
	}
	mBSplineBasis = v;
}

cBool BSplineCurve::IfBSplineMode() {
	return mBSplineMode;
}

cVoid BSplineCurve::SetIfBSplineMode(cBool v) {
	if (mBSplineMode && !v) {
		ConvertFromBSpline();
	} else if (!mBSplineMode && v) {
		ConvertToBSpline();
		if (mBSplineBasis) {
			CalculateBasis();
		}
	}
	mBSplineMode = v;
}

cVoid BSplineCurve::SwitchMode() {
	SetIfBSplineMode(!mBSplineMode);
}

cInt BSplineCurve::GetPrecision() {
	return mBasisPrecision;
}

cVoid BSplineCurve::SetPrecision(cInt prec) {
	mBasisPrecision = (prec <= 0) ? 1 : prec;
	CalculateBasis();
}

/*virtual*/ cVoid BSplineCurve::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color) {
	cInt i, size;
	Point4D *tab;
	PointsSet::PointPair *tab2;

	if (mBSplineMode) {
		size = this->mBSplineNodes.GetSize();
		tab = new Point4D[size];
		tab2 = new PointsSet::PointPair[size];
		for (i = 0; i < size; i++) {
			tab[i] = matrix * mMatrix * Point4D(this->mBSplineNodes[i].mPoint);
			tab2[i].mPoint = tab[i];
			tab2[i].mSelected = this->mBSplineNodes[i].mSelected;
		}
		if (mArePointsVisible) {
			PointsSet::Render(renderer, matrix, plane, color, tab2, size);
		}
		
		if (mBSplineBasis) {
			RenderSpline(renderer, matrix, plane, color, color, tab, size);
		} else {
			delete []tab;
			size = this->mBNodes.GetSize();
			tab = new Point4D[size];
			tab2 = new PointsSet::PointPair[size];

			for (i = 0; i < size; i++) {
				tab[i] = matrix * mMatrix * Point4D(this->mBNodes[i].mPoint);
				tab2[i].mPoint = tab[i];
				tab2[i].mSelected = this->mBNodes[i].mSelected;
			}
			BezierCurve::Render(renderer, matrix, plane, color, tab, size);
		}
	} else {
		size = this->mBNodes.GetSize();
		tab = new Point4D[size];
		tab2 = new PointsSet::PointPair[size];
		for (i = 0; i < size; i++) {
			tab[i] = matrix * mMatrix * Point4D(this->mBNodes[i].mPoint);
			tab2[i].mPoint = tab[i];
			tab2[i].mSelected = this->mBNodes[i].mSelected;
		}
		if (mArePointsVisible) {
			PointsSet::Render(renderer, matrix, plane, color, tab2, size);
		}
		BezierCurve::Render(renderer, matrix, plane, color, tab, size);
	}

	delete []tab;
	delete []tab2;
}

/*virtual*/ cVoid BSplineCurve::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) {
	cInt i, size;
	Point4D *tab;
	PointsSet::PointPair *tab2;

	if (mBSplineMode) {
		size = this->mBSplineNodes.GetSize();
		tab = new Point4D[size];
		tab2 = new PointsSet::PointPair[size];
		for (i = 0; i < size; i++) {
			tab[i] = matrix * mMatrix * Point4D(this->mBSplineNodes[i].mPoint);
			tab2[i].mPoint = tab[i];
			tab2[i].mSelected = this->mBSplineNodes[i].mSelected;
		}
		if (mArePointsVisible) {
			PointsSet::Render(renderer, matrix, plane, RGB(255, 255, 255), tab2, size);
		}
		
		if (mBSplineBasis) {
			RenderSpline(renderer, matrix, plane, mPolygonColor, mColor, tab, size);
		} else {
			delete []tab;
			size = this->mBNodes.GetSize();
			tab = new Point4D[size];
			tab2 = new PointsSet::PointPair[size];

			for (i = 0; i < size; i++) {
				tab[i] = matrix * mMatrix * Point4D(this->mBNodes[i].mPoint);
				tab2[i].mPoint = tab[i];
				tab2[i].mSelected = this->mBNodes[i].mSelected;
			}
			BezierCurve::Render(renderer, matrix, plane, tab, size);
		}
	} else {
		size = this->mBNodes.GetSize();
		tab = new Point4D[size];
		tab2 = new PointsSet::PointPair[size];
		for (i = 0; i < size; i++) {
			tab[i] = matrix * mMatrix * Point4D(this->mBNodes[i].mPoint);
			tab2[i].mPoint = tab[i];
			tab2[i].mSelected = this->mBNodes[i].mSelected;
		}
		if (mArePointsVisible) {
			PointsSet::Render(renderer, matrix, plane, RGB(255, 255, 255), tab2, size);
		}
		BezierCurve::Render(renderer, matrix, plane, tab, size);
	}

	delete []tab;
	delete []tab2;
}

cVoid BSplineCurve::ConvertFromBSpline() {
	PointPair node;
	node.mSelected = NO_SELECTION;

	mBNodes.Clear();
	if (mBSplineNodes.GetSize() < 4) {
		return;
	}
	node.mPoint = (mBSplineNodes[0].mPoint + mBSplineNodes[1].mPoint * 2.0f) / 3.0f;
	mBNodes.Add(node);

	for (cInt i = 1; i < mBSplineNodes.GetSize() - 1; i++)	{
		mBNodes.Add();

		node.mPoint = (mBSplineNodes[i].mPoint * 2.0f + mBSplineNodes[i + 1].mPoint) / 3.0f;
		mBNodes.Add(node);

		node.mPoint = (mBSplineNodes[i].mPoint + mBSplineNodes[i + 1].mPoint * 2.0f) / 3.0f;
		mBNodes.Add(node);

		cInt s = mBNodes.GetSize();
		mBNodes[s - 3].mSelected = NO_SELECTION;
		mBNodes[s - 3].mPoint = (mBNodes[s - 4].mPoint + mBNodes[s - 2].mPoint) / 2.0f;
	}

	mBNodes.Delete(mBNodes.GetSize() - 1);
	mBNodes.Delete(mBNodes.GetSize() - 1);
	mBNodes.Delete(0);
}

cVoid BSplineCurve::ConvertToBSpline()
{
	PointPair node;
	node.mSelected = NO_SELECTION;

	if (mBNodes.GetSize() < 4) {
		return;
	}
	mBSplineNodes.Clear();

	mBSplineNodes.Add();

	node.mPoint = mBNodes[1].mPoint*2.0f - mBNodes[2].mPoint;
	mBSplineNodes.Add(node);

	node.mPoint = mBNodes[0].mPoint*2.0f - mBNodes[1].mPoint;
	node.mPoint = node.mPoint*3.0f - mBSplineNodes[1].mPoint*2.0f;
	mBSplineNodes[0] = node;

	for (cInt i = 0; i < mBNodes.GetSize() - 3; i += 3)	{
		node.mPoint = mBNodes[i + 2].mPoint*2.0f - mBNodes[i + 1].mPoint;
		mBSplineNodes.Add(node);
	}

	node.mPoint = mBNodes[mBNodes.GetSize() - 1].mPoint*2.0f - mBNodes[mBNodes.GetSize() - 2].mPoint;
	node.mPoint = node.mPoint*3.0f - mBSplineNodes[mBSplineNodes.GetSize() - 1].mPoint*2.0f;
	mBSplineNodes.Add(node);
}

cVoid BSplineCurve::AddContPoints() {
	cInt size = mBNodes.GetSize();
	if (size < 3) {
		return;
	}

	PointPair node;
	node.mSelected = NO_SELECTION;
	Point4D a, b;

	node.mPoint = mBNodes[size - 1].mPoint*2.0f - mBNodes[size - 2].mPoint;
	mBNodes.Add(node);

	a = mBNodes[size - 2].mPoint*2.0f - mBNodes[size - 3].mPoint;
	node.mPoint = mBNodes[size].mPoint*2.0f - a;
	mBNodes.Add(node);
}

cVoid BSplineCurve::CalculateBasis() {
	if (mBSplineNodes.GetSize() < 4) {
		mBasis.Clear();
		return;
	}

	cInt s = mBasisPrecision*(mBSplineNodes.GetSize() - 3);
	cFloat t, dt = 1.0f/s;

	mBasis = DynamicArray<BSplineBasisW3>(s + 1);

	cFloat *T = new cFloat[mBSplineNodes.GetSize() + 4];
	t = 1.0f/(mBSplineNodes.GetSize() - 3);
	for (cInt i = 0; i < mBSplineNodes.GetSize() + 4; i++) {
		T[i] = t*(i - 3);
	}
	cInt j = 0;
	mBasis[j++] = CalculateN(T, 0.0f, 3);
	t = dt;
	for (cInt k = 0; k < mBSplineNodes.GetSize() - 3; k++) {
		for (cInt i = 0; i < mBasisPrecision; i++, t += dt) {
			mBasis[j++] = CalculateN(T, t, k + 3);
		}
	}

	delete []T;
}

BSplineCurve::BSplineBasisW3 BSplineCurve::CalculateN(cFloat *T, cFloat t, cInt i) {
	cFloat N[4], A[3], B[3], saved, term;
	BSplineBasisW3 bsb;
	cInt n = 3;

	N[0] = 1.0f;
	for (cInt j = 0; j < n; j++) {
		A[j] = T[i + j + 1] - t;
		B[j] = t - T[i - j];
		saved = 0.0f;
		for (cInt k = 0; k <= j; k++) {
			term = N[k]/(A[k] + B[j - k]);
			N[k] = saved + A[k]*term;
			saved = B[j - k]*term;
		}
		N[j + 1] = saved;
	}

	bsb.n0 = N[0];
	bsb.n1 = N[1];
	bsb.n2 = N[2];
	bsb.n3 = N[3];

	return bsb;
}

cVoid BSplineCurve::RenderSpline(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color, 
								 cWinColor polygonColor, Point4D *tab, cInt tabSize) {
	cInt iHalfWidth = renderer.GetWidth() >> 1, iHalfHeight = renderer.GetHeight() >> 1;
	if (tabSize < 4) {
		return;
	}
	AdaptBasis(tab, tabSize);

	cInt px, py, x, y, j = 0;
	BSplineBasisW3 temp;
	Point4D p;

	temp = mBasis[j++];
	p.mX = tab[0].mX*temp.n0 + tab[1].mX*temp.n1 + tab[2].mX*temp.n2 + tab[3].mX*temp.n3;
	p.mY = tab[0].mY*temp.n0 + tab[1].mY*temp.n1 + tab[2].mY*temp.n2 + tab[3].mY*temp.n3;
	p.mW = tab[0].mW*temp.n0 + tab[1].mW*temp.n1 + tab[2].mW*temp.n2 + tab[3].mW*temp.n3;
	px = (cInt)(p.mX/p.mW) + iHalfWidth;
	py = (cInt)(-p.mY/p.mW) + iHalfHeight;

	for (cInt k = 0; k < tabSize - 3; k++) {
		for (cInt i = 0; i < mBasisPrecision; i++) {
			temp = mBasis[j++];
			p.mX = tab[k].mX*temp.n0 + tab[k+1].mX*temp.n1 + tab[k+2].mX*temp.n2 + tab[k+3].mX*temp.n3;
			p.mY = tab[k].mY*temp.n0 + tab[k+1].mY*temp.n1 + tab[k+2].mY*temp.n2 + tab[k+3].mY*temp.n3;
			p.mW = tab[k].mW*temp.n0 + tab[k+1].mW*temp.n1 + tab[k+2].mW*temp.n2 + tab[k+3].mW*temp.n3;
			x = (cInt)(p.mX/p.mW) + iHalfWidth;
			y = (cInt)(-p.mY/p.mW) + iHalfHeight;
			if (p.mW >= plane.GetNearClippingPlane()) {
				renderer.DrawLine(px, py, x, y, color);
			}
			px = x; py = y;
		}
	}
	if (mIsPolygonVisible) {
		Point4D a, b;
		for (cInt i = 0; i < tabSize - 1; i++) {
			a = tab[i];
			b = tab[i + 1];
			if (Clip(a, b, plane))	{
				a.mX = (a.mX / a.mW) + iHalfWidth; 
				a.mY = (a.mY / -a.mW) + iHalfHeight;
				b.mX = (b.mX / b.mW) + iHalfWidth; 
				b.mY = (b.mY / -b.mW) + iHalfHeight;
				renderer.DrawROPLine(a, b, polygonColor);
			}
		}
	}
}

cVoid BSplineCurve::AdaptBasis(Point4D *tab, cInt tabSize) {
	Point4D prev = tab[0], curr;
	cFloat d = 0.0f, currd;
	cInt m;

	for (cInt i = 1; i < tabSize; i++) {
		curr = tab[i];
		currd = (curr - prev).Abs();
		if (i == 1 || i == tabSize - 1) {
			currd /= 3.0f;
		}
		if (currd > d) {
			d = currd;
		}
		prev = curr;
	}

	m = (cInt)(BSPLINE_M_PER_POINT*d);
	if (m < 1) {
		m = 1;
	}
	if (abs(mBasisPrecision - m) > BSPLINE_PREC_DIFF) {
		mBasisPrecision = m;
		CalculateBasis();
	}
}

cVoid BSplineCurve::SelectPoint(cInt iIndex, SELECTION_TYPE eSelected) {
	if (mBSplineMode) {
		if (iIndex >= 0 && iIndex < mBSplineNodes.GetSize()) {
			if (mBSplineNodes[iIndex].mSelected != PERS_SELECTION) {
				mBSplineNodes[iIndex].mSelected = eSelected;
			}
		}
	} else {
		if (iIndex >= 0 && iIndex < mBNodes.GetSize()) {
			if (mBNodes[iIndex].mSelected != PERS_SELECTION) {
				mBNodes[iIndex].mSelected = eSelected;
			}
		}
	}
}

PointsSet::SELECTION_TYPE BSplineCurve::SelectPoint(cInt iIndex, cBool bSelect) {
	if (mBSplineMode) {
		switch(mBSplineNodes[iIndex].mSelected) {
			case NO_SELECTION:
			case TEMP_SELECTION:
				mBSplineNodes[iIndex].mSelected = PERS_SELECTION;
				break;
			default:
				mBSplineNodes[iIndex].mSelected = TEMP_SELECTION;
				break;
		}
		return mBSplineNodes[iIndex].mSelected;
	} else {
		switch(mBNodes[iIndex].mSelected) {
			case NO_SELECTION:
			case TEMP_SELECTION:
				mBNodes[iIndex].mSelected = PERS_SELECTION;
				break;
			default:
				mBNodes[iIndex].mSelected = TEMP_SELECTION;
				break;
		}
		return mBNodes[iIndex].mSelected;
	}
}

cVoid BSplineCurve::DeselectPoints() {
	if (mBSplineMode) {
		for (cInt i = 0; i < mBSplineNodes.GetSize(); i++) {
			mBSplineNodes[i].mSelected = NO_SELECTION;
		}
	} else {
		for (cInt i = 0; i < mBNodes.GetSize(); i++) {
			mBNodes[i].mSelected = NO_SELECTION;
		}
	}
}

/*virtual*/ cChar* BSplineCurve::GetName() {
	return "BSplineCurve";
}

/*virtual*/ cVoid BSplineCurve::DisplayInfo(Renderer& renderer) {
	cChar pcBuff [MAX_BUF_SIZE];
	cInt iLineNr = 0;

	sprintf_s(pcBuff, "B-Spline Curve Info:");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, mPolygonColor);
	sprintf_s(pcBuff, "Nr of points :");	
	renderer.DrawString(H_TXT_SPACER, iLineNr * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "%3i", mBSplineMode ? mBSplineNodes.GetSize() : mBNodes.GetSize());	
	renderer.DrawString(H_TXT_SPACER * 13, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "Design Mode :");	
	renderer.DrawString(H_TXT_SPACER, iLineNr * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "%s", mBSplineMode ? "B-Spline" : "Bernstein");	
	renderer.DrawString(H_TXT_SPACER * 15, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, Color::ToWinColor(WHITE));
	iLineNr++;
	sprintf_s(pcBuff, "[Ctrl + K] - Change Design Mode");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + L] - Show/Hide Polygon");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + V] - Show/Hide Points Coordinates");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "Space - Deselect Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "LMB - Select/Deselect Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "RMB - Add Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "DEL - Delete Selected Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "LMB & Drag - Move Selected Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
}

cVoid BSplineCurve::DisplayInfo(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) { 
	cChar buf[MAX_BUF_SIZE];
	Point4D a;
	Matrix4D m = matrix * mMatrix * mObjectMatrix;

	if (mDisplayPointsCords) {
		if (mBSplineMode) {
			for (cInt i = 0; i < mBSplineNodes.GetSize(); i++) {
				a = mBSplineNodes[i].mPoint;
				a = m * a;
				if (a.mW < plane.GetNearClippingPlane()) {
					continue;
				}
				a.Normalize();
				a.mX += (renderer.GetWidth() >> 1);
				a.mY = -a.mY + (renderer.GetHeight() >> 1);
				sprintf_s(buf, "[%.0f, %.0f, %.0f]", mBSplineNodes[i].mPoint.mX,
						mBSplineNodes[i].mPoint.mY,  mBSplineNodes[i].mPoint.mZ);
				renderer.DrawString((cInt) a.mX, (cInt) a.mY, buf, 
					(mBSplineNodes[i].mSelected == NO_SELECTION) ? false : true, true, false);
			}
		} else {
			for (cInt i = 0; i < mBNodes.GetSize(); i++) {
				a = mBNodes[i].mPoint;
				a = m * a;
				if (a.mW < plane.GetNearClippingPlane()) {
					continue;
				}
				a.Normalize();
				a.mX += (renderer.GetWidth() >> 1);
				a.mY = -a.mY + (renderer.GetHeight() >> 1);
				sprintf_s(buf, "[%.0f, %.0f, %.0f]", mBNodes[i].mPoint.mX,
						mBNodes[i].mPoint.mY,  mBNodes[i].mPoint.mZ);
				renderer.DrawString((cInt) a.mX, (cInt) a.mY, buf, 
					(mBNodes[i].mSelected == NO_SELECTION) ? false : true, true, false);
			}
		}
	}
}